#include <map>
#include <stdio.h>

#ifdef SFML
#include <SFML/Window.hpp>
#elif defined SDL
#include <SDL.h>
#elif defined NDS
#include <nds.h>
#endif // #ifdef SFML

#include "input.h"


#ifdef SFML
extern sf::Window &__App;
static sf::Window &App = __App;
#elif defined SDL

#endif // #ifdef SFML

static std::string textBuffer;
static bool quit = false; // whether quit has been requested
static v2<int> cursor(0, 0);
static bool pen_down = false; // also used for left mouse button
static u8 key_state[input::_KEY_COUNT+1];
#ifdef SFML
static std::map<sf::Key::Code, input::key> key_map;

static void HandleKeyEvent(sf::Event &event, bool pressed)
{
  if(pressed) key_state[key_map[event.Key.Code]] |= pressed;
  else key_state[key_map[event.Key.Code]] &= 2;
}
static void HandleEvent(sf::Event &event)
{
  switch(event.Type)
  {
    case sf::Event::Closed: quit = true; break;
    case sf::Event::KeyPressed: HandleKeyEvent(event, true); break;
    case sf::Event::KeyReleased: HandleKeyEvent(event, false); break;
    case sf::Event::MouseMoved: cursor = v2<int>(event.MouseMove.X, event.MouseMove.Y); break;
    case sf::Event::MouseButtonPressed:
      if(event.MouseButton.Button==sf::Mouse::Left)
      {
        pen_down = true;
      }
      break;
    case sf::Event::MouseButtonReleased:
      if(event.MouseButton.Button==sf::Mouse::Left)
      {
        pen_down = false;
      }
      break;
  }
}
#elif defined SDL
static std::map<SDLKey, input::key> key_map;

static void HandleKeyEvent(SDL_Event event, bool pressed)
{
  if(event.key.keysym.unicode) textBuffer += (char)event.key.keysym.unicode;
  if(pressed) key_state[key_map[event.key.keysym.sym]] |= pressed;
  else key_state[key_map[event.key.keysym.sym]] &= 2;
}
static void HandleEvent(SDL_Event &event)
{
  switch(event.type)
  {
    case SDL_QUIT: quit = true; break;
    case SDL_KEYDOWN: HandleKeyEvent(event, true); break;
    case SDL_KEYUP: HandleKeyEvent(event, false); break;
    case SDL_MOUSEMOTION: cursor = v2<int>(event.motion.x, event.motion.y); break;
    case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP:
      if(event.button.button==SDL_BUTTON_LEFT)
      {
        pen_down = event.button.state==SDL_PRESSED;
      }
      break;
  }
}
#elif defined NDS
static std::map<int, input::key> key_map;
#endif // #ifdef SFML

void input::update()
{
  for(int i=0;i<input::_KEY_COUNT;i++) key_state[i] = (key_state[i]<<1)|(key_state[i]&1);

#ifdef SFML
  sf::Event event;

  while(App.GetEvent(event)) HandleEvent(event);
#elif defined SDL
  SDL_Event event;

  while(SDL_PollEvent(&event)) HandleEvent(event);
#elif defined NDS
  scanKeys();
#endif // #ifdef SFML
}

void input::reset()
{
  for(int i=0;i<input::_KEY_COUNT;i++) key_state[i] = 0;
#ifdef SFML
  App.EnableKeyRepeat(false);

  key_map[sf::Key::Left] = input::KEY_LEFT;
  key_map[sf::Key::Up] = input::KEY_UP;
  key_map[sf::Key::Right] = input::KEY_RIGHT;
  key_map[sf::Key::Down] = input::KEY_DOWN;
  key_map[sf::Key::Return] = input::KEY_START;
  key_map[sf::Key::Space] = input::KEY_SELECT;
  key_map[sf::Key::Escape] = input::KEY_ESCAPE;
#elif defined SDL
  key_map[SDLK_LEFT] = input::KEY_LEFT;
  key_map[SDLK_UP] = input::KEY_UP;
  key_map[SDLK_RIGHT] = input::KEY_RIGHT;
  key_map[SDLK_DOWN] = input::KEY_DOWN;
  key_map[SDLK_RETURN] = input::KEY_START;
  key_map[SDLK_SPACE] = input::KEY_SELECT;
  key_map[SDLK_ESCAPE] = input::KEY_ESCAPE;
#elif defined NDS
  /* key_map[KEY_LEFT] = Input::KEY_LEFT;
  key_map[KEY_UP] = Input::KEY_UP;
  key_map[KEY_RIGHT] = Input::KEY_RIGHT;
  key_map[KEY_DOWN] = Input::KEY_DOWN;
  key_map[KEY_START] = Input::KEY_START;
  key_map[KEY_SELECT] = Input::KEY_SELECT; */
  //key_map[SDLK_ESCAPE] = Input::KEY_ESCAPE;
  key_map[input::KEY_LEFT] = KEY_LEFT;
  key_map[input::KEY_UP] = KEY_UP;
  key_map[input::KEY_RIGHT] = KEY_RIGHT;
  key_map[input::KEY_DOWN] = KEY_DOWN;
  key_map[input::KEY_START] = KEY_START;
  key_map[input::KEY_SELECT] = KEY_SELECT;
#endif // #ifdef SFML
}

bool input::isQuitRequested()
{
  return quit;
}

bool input::isKeyDown(key pKey)
{
#ifdef NDS
  return keysHeld()&key_map[pKey];
#else
  return (key_state[pKey]&1)!=0;
#endif // #ifdef NDS
}
bool input::isKeyUp(key pKey)
{
#ifdef NDS
  return !(keysHeld()&key_map[pKey]);
#else
  return (key_state[pKey]&1)==0;
#endif // #ifdef NDS
}
bool input::isKeyPressed(key pKey)
{
#ifdef NDS
  return keysDown()&key_map[pKey];
#else
  return (key_state[pKey]&3)==1;
#endif // #ifdef NDS
}
bool input::isKeyReleased(key pKey)
{
#ifdef NDS
  return keysUp()&key_map[pKey];
#else
  return (key_state[pKey]&3)==2;
#endif // #ifdef NDS
}

bool input::getCursor(v2<int> &pos)
{
#ifdef NDS
  touchPosition data;
  touchRead(&data);
  pos = v2<int>(data.px, data.py);
  return keysHeld()&KEY_TOUCH;
#else
  pos = cursor;
  return pen_down;
#endif // #ifdef NDS
}

std::string input::getTextBuffer()
{
  return textBuffer;
}

void input::clearTextBuffer()
{
  textBuffer.clear();
}
